//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖保佑             永无BUG 
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//                  不见满街漂亮妹，哪个归得程序员？  


/* ****************************************************************************
 * Author:      671643387
 * Date:        2015/9/22
 * Description: 英雄志
 * ***************************************************************************/

function HeroLog() {
    this.logs = {};
    this.updates = {};
    this.dirtyCount = 0;
}

HeroLog.create = function(){
    gDBWorld.insert({_id:"hero_log",
        hero_log:{
        }
    }, function(err, result){});
};

exports.HeroLog = HeroLog;

HeroLog.prototype = {
    /**
     * 初始化英雄志
     * @param fn 回调函数
     */
    init: function(fn){
        gDBWorld.findOne({_id : 'hero_log'}, {}, function(err, doc){
            if (err) {
                fn(err);
            } else {
                if( doc ) {
                    this.logs = doc.hero_log;
                }
                fn();
            }
        }.bind(this));
    },

    /**
     * 修改数据
     * @param uid   要修改数据的玩家UID
     * @param data  数据
     */
    markDirty: function(uid, data){
        this.logs[uid] = data;
        this.updates[util.format('hero_log.%d', uid)] = data;
        this.dirtyCount++;
    },

    /**
     * 保存数据到数据库
     * @param force 是否强制保存（默认数据会缓存到一定量才写入数据库）
     * @param fn    回调函数
     */
    save: function(force, fn){
        // TODO: 优化
        if (!force && (this.dirtyCount < 1)) {
            fn && fn(true);
        } else {
            var updates = this.updates;
            this.updates = {};
            this.dirtyCount = 0;

            gDBWorld.update({_id : 'hero_log'}, {$set:updates}, true, function(err, result){
                if( err ) {
                    ERROR(util.format('SAVE HEROLOG: %j %j', updates, err));
                    fn && fn(false);
                }else {
                    LOG(util.format('SAVE HEROLOG: %j', updates));
                    fn && fn(true);
                }
            });
        }
    },

    /**
     * 获取指定UID的角色的英雄志数据
     * @param player
     */
    getByUid: function(player) {
        var userHeroLog = player.user.hero_log;
        if (userHeroLog) {
            if (Date.getDayPassed(userHeroLog.timeStamp) != 1) {

                userHeroLog.timeStamp = Date.getStamp();
                userHeroLog.placement_specialAward = 0;
                userHeroLog.placement_num = gConfGlobal.HeroLogPlacementNum;
                userHeroLog.placement_buy_num = 0;
                userHeroLog.placement_heros = {};

                // 更新章节放置玩法
                for (var chapId in userHeroLog.chapters) {
                    if (+chapId > +gConfGlobal.HeroChapterMaxId) {
                        break;
                    }

                    var chapter = userHeroLog.chapters[chapId];
                    chapter.placement["heros"] = [0,0,0];

                    var chapterRem = this._calc_placement_recommand(+chapId);;
                    chapter.placement["rHeros"] = chapterRem.rHeros;
                    chapter.placement["awards"] = chapterRem.awards;

                    if (this._calc_placement_special(userHeroLog, chapter,
                            gConfHeroLogPlacement[chapId])) {
                        userHeroLog.placement_specialAward = chapId;
                    }
                }

                player.markDirty("hero_log");
            }
            return userHeroLog;
        }

        // 生成新数据
        userHeroLog = {
            timeStamp: Date.getStamp(),         // 时间戳，用于每天更新数据

            progress: {                         // 闯关进度(下一个可以打的关卡)
                chapter: 1,                     // 章节
                checkpoint: 1                   // 关卡
            },

            chapters:{                          // 章节数据
                1: {}                           // 章节1
            },

            dragonball:0,                       // 获取的龙珠数量

            liulele: {
                Attack: 0,
                Health: 0,
                Defence: 0,
                MagicDefence: 0
            },                        // 刘乐乐属性加成

            placement_specialAward: 0,                     // 今日特殊奖励章节ID
            placement_num: gConfGlobal.HeroLogPlacementNum, // 今日可放置次数
            placement_buy_num: 0,                           // 今日购买的放置次数
            placement_heros: {}                             // 今日已放置武将
        };
        this._initChapter(userHeroLog.chapters[1]);
        player.user.hero_log = userHeroLog;
        player.markDirty("hero_log");

        return userHeroLog;
    },

    /**
     * 初始化章节数据
     * @param chapter   章节
     * @private
     */
    _initChapter: function(chapter){
        chapter[1] = {                  // 关卡1数据
            state:0,                   // 状态(0:未打,1:普通未打赢,2:普通打赢了,3:困难未打赢,4:困难打赢了)
        };
        chapter[2] = {                  // 关卡2数据
            state:0,
        };
        chapter[3] = {                  // 关卡3数据
            state:0,
        };
        chapter[4] = {                  // 关卡4数据
            state:0,
        };
        chapter[5] = {                  // 关卡5数据
            state:0,
        };
        chapter[6] = {                  // 关卡6数据
            state:0,
        };
        chapter[7] = {                  // 关卡7数据
            state:0,
        };
        chapter[8] = {                  // 关卡8数据
            state:0,
        };

        chapter["animState"] = 0;       // 章节动画状态(0:未播放,1:已播放)
        chapter["star"] = 0;            // 章节星数
        chapter["star_award"] = 0;      // 章节奖励领取情况(4,8,12,16)

        chapter["placement"] = {
            state: 0,                   // 放置玩法开启状态(0:未开启,1:已开启)
            rHeros: [0,0],              // 今日推荐的英雄
            heros: [0,0,0],             // 放置的英雄[主将，副将1，副将2]
            awards: []                  // 奖励[主要奖励，次要奖励1，次要奖励2]
        };
    },

    /**
     * 战斗
     * @param player
     * @param chapId    章节ID(1-~)
     * @param chkptId   关卡ID(1-8)
     * @param fn
     */
    battle: function(player, chapId, chkptId, fn){
        var heroLog = this.getByUid(player);

        //// 战前检查
        if ((chapId > heroLog.progress.chapter)
            || ((chapId == heroLog.progress.chapter)
            && (chkptId > heroLog.progress.checkpoint))) {
            fn("can not skip some checkpoint");
            return;
        }

        if (chapId != 1) {
            // 检查上章节章节动画是否播放
            var lastChapter = heroLog.chapters[chapId - 1];
            if (lastChapter.animState == 0) {
                fn("must play last chapter's animation");
                return;
            }
        }

        var chkptLevel = 1; // 默认普通模式

        //// 取关卡数据
        var chkpt = heroLog.chapters[chapId][chkptId];

        // 选择难度
        switch (chkpt.state) {
            case 0:
            case 1:
                // 打普通关卡
                break;

            case 2:
            case 3:
                // 打困难关卡
                chkptLevel = 2;
                break;
            case 4:
                // 本关卡已经通关
                fn("this checkpoint is clearanced");
                return;
        }

        // 取关卡配置
        var chkptConf = gConfHeroLog[chapId][chkptId][chkptLevel];

        // 判断消耗品够不够
        var needFood = chkptConf["Food"];
        if (player.user.status.food < needFood) {
            fn("you do not have enught food");
            return;
        }

        // 取怪物阵形
        var battleConf = {
            UserLevel: chkptConf["UserLevel"],
            Boss: chkptConf["Boss"],
            PetLevel: chkptConf["PetLevel"],
            PetSkillLevels: chkptConf["PetSkillLevels"],
            PetSkills: chkptConf["PetSkills"],

            Pos0: chkptConf["Pos0"],
            Pos1: chkptConf["Pos1"],
            Pos2: chkptConf["Pos2"],
            Pos3: chkptConf["Pos3"],
            Pos4: chkptConf["Pos4"],
            Pos5: chkptConf["Pos5"],
            Pos6: chkptConf["Pos6"],
            Pos7: chkptConf["Pos7"],
            Pos8: chkptConf["Pos8"],
        };

        // 战斗
        var attacker = new Fighter();
        var defender = new Fighter();
        attacker.initWithPlayer(player);
        defender.initWithHeroLogMonster(battleConf);

        var battleReport = attacker.fight(defender);

        var ret = {
            battle: battleReport        // 战报
        };

        if (battleReport.success) {
            //// 战斗胜利
            player.costResource("food", needFood);
            ret.food = -needFood;

            switch (chkpt.state) {
                case 0:
                case 1:
                    chkpt.state = 2;
                    break;

                case 2:
                case 3:
                    chkpt.state = 4;
                    break;
            }

            if (chkptId == 8 && chkptLevel == 1) {
                // 普通关卡每章最后一关
                var newChapterId = chapId + 1;

                // 初始化下一章节
                heroLog.chapters[newChapterId] = {};
                this._initChapter(heroLog.chapters[newChapterId]);

                // 更新进度
                heroLog.progress.chapter = newChapterId;
                heroLog.progress.checkpoint = 1;
            } else if (chkptLevel == 1) {
                // 更新进度
                heroLog.progress.checkpoint = chkptId + 1;
            }

            // 统计星数
            heroLog.chapters[chapId].star = heroLog.chapters[chapId].star + 1;
            ret.star = heroLog.chapters[chapId].star;

            if (chkptConf.DragonBallAttr) {
                var attrs = chkptConf.DragonBallAttr.split(':');
                if (attrs.length == 2) {
                    // 龙珠
                    var attrName = attrs[0];
                    heroLog.liulele[attrName] += +attrs[1];
                    ++heroLog.dragonball;
                    player.roleDirty = true;

                    ret.dragonball = heroLog.dragonball;
                    ret.dragonball_attr = {};
                    ret.dragonball_attr[attrName] = heroLog.liulele[attrName];
                }
            }

            // 掉落道具
            ret.awards = player.addAwards([chkptConf.Award1, chkptConf.Award2, chkptConf.Award3]);
        } else {
            //// 战斗失败
            switch (chkpt.state) {
                case 0:
                    chkpt.state = 1;
                    break;
                case 2:
                    chkpt.state = 3;
                    break;
            }
        }

        //// 返回变更的数据
        ret.checkpoint = {
            state: chkpt.state
        };

        // 保存数据
        player.markDirty("hero_log");
        if (chkpt.state == 4) {
            player.checkEvent("HeroLogLevel2", chapId);
        }

        fn(null, ret);
    },

    /**
     * 播放章节动画
     * @param player
     * @param chapId    章节ID
     * @param fn
     */
    play_chapter_animation: function(player, chapId, fn){
        var heroLog = this.getByUid(player);

        // 检查是否可以播放动画
        if (chapId < heroLog.progress.chapter) {
            var ret = {};
            if (heroLog.chapters[chapId]["animState"] == 0) {
                heroLog.chapters[chapId]["animState"] = 1;
                heroLog.chapters[chapId]["placement"]["state"] = 1;  // 开启当前章节放置玩法

                // 计算今日放置数据
                var chapterRem = this._calc_placement_recommand(chapId);
                heroLog.chapters[chapId]["placement"]["rHeros"] = chapterRem.rHeros;
                heroLog.chapters[chapId]["placement"]["awards"] = chapterRem.awards;
                if (this._calc_placement_special(heroLog, heroLog.chapters[chapId],
                        gConfHeroLogPlacement[chapId])) {
                    heroLog.placement_specialAward = chapId;
                    ret["placement_specialAward"] = chapId;
                }

                ret["animState"] = 1;
                ret["placement"] = heroLog.chapters[chapId]["placement"];
                ret["chapter"] = heroLog.chapters[chapId + 1];

                player.markDirty("hero_log");
            }
            fn(null, ret);
            return;
        }

        fn("can not play chapter animation");
    },

    /**
     * 放置玩法
     * @param player
     * @param chapId
     * @param mainRole
     * @param rRole1
     * @param rRole2
     * @param fn
     */
    placement: function(player, chapId, mainRole, rRole1, rRole2, fn){
        var placementConf = gConfHeroLogPlacement[chapId];
        var heroLog = this.getByUid(player);
        var chapter = heroLog.chapters[chapId];

        //// 检查
        if (heroLog.placement_num <= 0) {
            fn("your placement num used up");
            return;
        }

        if (chapId >= heroLog.progress.chapter) {
            fn("you can not placement in this chapter cos it not clearanced");
            return;
        }

        // 检查放置状态
        if (chapter.placement["state"] == 0) {
            fn("placement of this chapter is not open");
            return;
        }
        if (chapter.placement["heros"][0] != 0) {
            fn("already placement in this chapter");
            return;
        }

        // 检查是否拥有主将
        var masterId = gConfHeroLog[chapId][1][1].MasterRole;
        if (!player.user.role[masterId]) {
            fn("you don not have the master hero of this chapter");
            return;
        }

        // 检查武将是否已经放置过了
        if (mainRole != 0 && heroLog.placement_heros[mainRole]) {
            fn("hero " + mainRole + " already placement today");
            return;
        }
        if (rRole1 != 0 && heroLog.placement_heros[rRole1]) {
            fn("hero " + rRole1 + " already placement today");
            return;
        }
        if (rRole2 != 0 && heroLog.placement_heros[rRole2]) {
            fn("hero " + rRole2 + " already placement today");
            return;
        }

        chapter.placement.heros = [
            mainRole,
            rRole1,
            rRole2
        ];

        // 减少放置次数
        --heroLog.placement_num;

        var placement_heros = {};

        // 记录已放置武将
        if (mainRole != 0) {
            heroLog.placement_heros[mainRole] = 1;
            placement_heros[mainRole] = 1;
        }
        if (rRole1 != 0) {
            heroLog.placement_heros[rRole1] = 1;
            placement_heros[rRole1] = 1;
        }
        if (rRole2 != 0) {
            heroLog.placement_heros[rRole2] = 1;
            placement_heros[rRole2] = 1;
        }

        player.markDirty("hero_log");

        // 放置奖励配置
        var award1 = clone(chapter.placement.awards[0]);
        var award2 = clone(chapter.placement.awards[1]);
        var award3 = clone(chapter.placement.awards[2]);

        // 判断放置的是否是推荐武将
        var factor = 2;
        var rHeros = chapter.placement.rHeros;
        if (mainRole == masterId) {
            award1[2] = award1[2] * factor;

            // 判断特殊奖励
            if (heroLog.placement_specialAward == chapId) {
                award1[2] = award1[2] * 2;
            }
        }
        if (rRole1 == rHeros[0]) {
            award2[2] = award2[2] * factor;
        }
        if (rRole2 == rHeros[1]) {
            award3[2] = award3[2] * factor;
        }

        var awards = [];

        if (mainRole != 0) {
            awards.push(award1);
        }
        if (rRole1 != 0) {
            awards.push(award2);
        }
        if (rRole2 != 0) {
            awards.push(award3);
        }

        // 返回变更数据和奖励
        var ret = {
            heros: chapter.placement.heros,
            //placement_heros: placement_heros,
            placement_num: heroLog.placement_num
        };
        ret.awards = player.addAwards(awards);
        fn(null, ret);
    },

    /**
     * 购买放置次数
     * @param player
     * @param fn
     */
    buy: function(player, fn) {
        var heroLog = this.getByUid(player);

        var confBuy = gConfBuy[heroLog.placement_buy_num + 1];
        if( !confBuy ) {
            fn('no more to buy');
            return;
        }

        var costCash = +confBuy.CashHerolog;
        if( !costCash ) {
            fn('no more to buy');
            return;
        }

        if (!player.costResource("cash", costCash)) {
            fn("you need more cash");
            return;
        }

        // 购买成功
        ++heroLog.placement_buy_num;
        ++heroLog.placement_num;
        // 清空放置的历史记录
        heroLog.placement_heros = {};

        player.markDirty("hero_log");

        fn(null, {
            placement_num: heroLog.placement_num,
            placement_buy_num: heroLog.placement_buy_num,
            cash: -costCash
        });
    },

    /**
     * 领取章节奖励
     * @param player
     * @param chapId
     * @param fn
     */
    chapter_award: function(player, chapId, fn){
        var heroLog = this.getByUid(player);
        var chapter = heroLog.chapters[chapId];

        if (!chapter) {
            fn("no data");
            return;
        }

        if (chapter.star_award >= 16) {
            fn("all award was got");
            return;
        }

        var awardStar = chapter.star_award + 4;
        if (chapter.star < awardStar) {
            fn("you can not get award cos you don not have enught star");
            return;
        }

        var awardId = Math.floor(awardStar / 4);
        var awards = gConfHeroLogChapterAward[chapId]["Awards" + awardId];

        chapter.star_award = awardStar;
        player.markDirty("hero_log");

        var ret = {};
        ret.awards = player.addAwards(awards);
        ret.star_award = awardStar;
        fn(null, ret);
    },

    /**
     * 放置随机
     * @param chapId
     * @returns {*}
     * @private
     */
    _calc_placement_recommand: function(chapId){
        var rem = {};
        var placementConf = gConfHeroLogPlacement[chapId];

        function random_hero(heros, exclude) {
            exclude = exclude || {};
            var weightSum = 0;
            var weightMap = {};
            for (var index=0; index < heros.length; ++index) {
                if (index in exclude)
                    continue;
                weightSum += +heros[index][1];
                weightMap[weightSum] = index;
            }
            var randVal = Math.floor(Math.random() * weightSum);
            for (var weight in weightMap) {
                if (randVal <= +weight) {
                    var index = weightMap[weight];
                    return {
                        index: index,
                        hero: heros[index][0]
                    };
                }
            }
            return null;
        }

        // 随机推荐武将
        var rHeros = [];
        var excludeHero = {};

        var rHero = random_hero(placementConf.RHeros, excludeHero);
        rHeros.push(rHero["hero"]);

        excludeHero[rHero["index"]] = 1;
        rHero = random_hero(placementConf.RHeros, excludeHero);
        rHeros.push(rHero["hero"]);

        rem["rHeros"] = rHeros;

        // 随机奖励
        rem["awards"] = this._random_placement_award(placementConf);

        return rem;
    },

    /**
     * 随机放置奖励
     * @param placementConf
     * @returns {Array.<*>}
     * @private
     */
    _random_placement_award: function(placementConf) {
        function random_award(awards, exclude) {
            exclude = exclude || {};
            var weightSum = 0;
            var weightMap = {};
            for (var index=0; index < awards.length; ++index) {
                if (index in exclude)
                    continue;
                weightSum += +awards[index][1];
                weightMap[weightSum] = index;
            }
            var randVal = Math.floor(Math.random() * weightSum);
            for (var weight in weightMap) {
                if (randVal <= +weight) {
                    var index = weightMap[weight];
                    return {
                        index: index,
                        award: awards[index][0]
                    };
                }
            }
            return null;
        }

        // 取主将奖励
        var masterAward = random_award(placementConf["MasterAwards_"])["award"];

        // 取辅将奖励
        var slaverAwards = [];
        var slaverAward = random_award(placementConf["SlaverAwards_"]);
        slaverAwards.push(slaverAward["award"]);
        var exclude = {};
        exclude[slaverAward["index"]] = 1;
        slaverAward = random_award(placementConf["SlaverAwards_"], exclude);
        slaverAwards.push(slaverAward["award"]);

        return [masterAward].concat(slaverAwards);
    },

    /**
     * 计算今日特殊奖励
     * @param chapter
     * @private
     */
    _calc_placement_special:function(heroLog, chapter, placmentConf){
        // 今日已经有特殊奖励章节
        if (heroLog.placement_specialAward != 0)
            return false;

        // 今日奖励是珍稀奖励
        if (+chapter.placement.awards[0][1] == +placmentConf.RareMaterialID) {
            return false;
        }

        return (Math.floor(Math.random() * 100) <= placmentConf.SpecialOdds);
    },

    clear: function(player, fn){
        player.user.hero_log = undefined;
        player.markDirty("hero_log");
        fn(null, {});
    },

    skip: function(player, chapId) {
        var heroLog = this.getByUid(player);

        heroLog.progress.chapter = chapId;
        heroLog.progress.checkpoint = 1;

        for (var chap = 1; chap < chapId; ++chap) {
            var chapter = heroLog.chapters[chap] || {};

            for (var chk = 1; chk <= 8; ++chk) {
                var checkpoint = chapter[chk] || { state:0 };
                if (checkpoint.state == 0 || checkpoint.state == 1)
                    checkpoint.state = 2;
                chapter[chk] = checkpoint;
            }

            chapter.animState = 1;
            chapter.star = 0;
            chapter.star_award = 0;
            chapter.placement = {
                state: 1,
                rHeros: [0,0],
                heros: [0,0,0],
                awards: []
            };

            var chapterRem = this._calc_placement_recommand(+chap);;
            chapter.placement["rHeros"] = chapterRem.rHeros;
            chapter.placement["awards"] = chapterRem.awards;

            heroLog.chapters[chap] = chapter;
        }

        heroLog.chapters[chapId] = {};
        this._initChapter(heroLog.chapters[chapId]);

        player.markDirty("hero_log");
    }
};